﻿/*********************************************************************************
 *Author:         OnClick
 *Version:        0.0.1
 *UnityVersion:   2017.2.3p3
 *Date:           2019-08-13
 *Description:    IFramework
 *History:        2018.11--
*********************************************************************************/
using XLua;
using UnityEngine;
using System.Collections.Generic;
using System;
using IFramework.Singleton;

namespace IFramework.Hotfix.Lua
{
    public class XLuaEnv : SingletonPropertyClass<XLuaEnv>
    {
        private List<IXLuaDisposable> disposes = new List<IXLuaDisposable>();
        public static void Subscribe(IXLuaDisposable dispose)
        {
            if (!instance.disposes.Contains(dispose))
            {
                instance.disposes.Add(dispose);
            }
        }
        public static void UnSubscribe(IXLuaDisposable dispose)
        {
            if (instance.disposes.Contains(dispose))
            {
                instance.disposes.Remove(dispose);
            }
        }
        private XLuaEnv() { }
        private LuaEnv _luaenv;
        private List<LuaTable> _tables;
        private static float _lastGCTime;

        public static LuaTable gtable { get { return instance._luaenv.Global; } }
        public static float gcInterval = 1f;
        public static bool available { get; private set; }

        public static LuaTable NewTable()
        {
            LuaTable table = instance._luaenv.NewTable();
            instance._tables.Add(table);
            return table;
        }
        public static void AddLoader(IXLuaLoader loader)
        {
            instance._luaenv.AddLoader(loader.load);
        }

        public static LuaFunction LoadString(string chunk, string chunkName = "chunk", LuaTable env = null)
        {
            return instance._luaenv.LoadString(chunk, chunkName, env);
        }
        public static T LoadString<T>(string chunk, string chunkName = "chunk", LuaTable env = null)
        {
            return instance._luaenv.LoadString<T>(chunk, chunkName, env);
        }
        public static T LoadString<T>(byte[] chunk, string chunkName = "chunk", LuaTable env = null)
        {
            return instance._luaenv.LoadString<T>(chunk, chunkName, env);
        }



        public static object[] DoString(byte[] chunk, string chunkName = "chunk", LuaTable env = null)
        {
            return instance._luaenv.DoString(chunk, chunkName, env);
        }
        public static object[] DoString(string chunk, string chunkName = "chunk", LuaTable env = null)
        {
            return instance._luaenv.DoString(chunk, chunkName, env);
        }



        public static LuaTable GetTable(TextAsset luaScript, string chunkName = "chunk")
        {
            //// 为每个脚本设置一个独立的环境，可一定程度上防止脚本间全局变量、函数冲突
            LuaTable meta = XLuaEnv.NewTable();
            meta.Set("__index", XLuaEnv.gtable);
            LuaTable scriptEnv = XLuaEnv.NewTable();
            scriptEnv.SetMetaTable(meta);
            meta.Dispose();
            DoString(luaScript.text, chunkName, scriptEnv);
            return scriptEnv;
        }


        public static void FullGc()
        {
            instance._luaenv.FullGc();
        }

        protected override void OnSingletonInit()
        {
            available = true;
            _luaenv = new LuaEnv();
            _tables = new List<LuaTable>();
            Launcher.env.BindUpdate(Update);
            Launcher.env.BindDispose(() => { SingletonCollection.Dispose<XLuaEnv>(); });
        }
        protected override void OnDispose()
        {
            for (int i = disposes.Count - 1; i >= 0; i--)
            {
                if (disposes[i] != null)
                {
                    disposes[i].LuaDispose();
                }
            }
            available = false;
            _tables.ForEach((table) =>
            {
                table.Dispose();
            });
            // DoString(@"
            //         local util = require 'xlua.util'
            //        -- util.print_func_ref_by_csharp()
            // ");
            _luaenv.Dispose();
            _luaenv = null;
        }

        private void Update()
        {
            if (_luaenv == null) return;
            if (Time.time - _lastGCTime > gcInterval)
            {
                _luaenv.Tick();
                _lastGCTime = Time.time;
            }
        }

    }
}
